package cn.buddha.jec.flowable.core;

import cn.buddha.jec.exception.TongBusinessResult;
import cn.buddha.jec.flowable.handler.FlowableExceptionHandler;
import cn.buddha.jec.flowable.handler.PostHandler;
import cn.buddha.jec.flowable.handler.PreHandler;
import cn.buddha.jec.flowable.handler.business.BusinessExtraFactory;
import cn.buddha.jec.flowable.handler.business.IBusinessPostHandler;
import cn.buddha.jec.flowable.handler.business.IBusinessPreHandler;
import cn.buddha.jec.model.TongBaseRequest;
import cn.buddha.jec.model.TongBaseResponse;
import cn.buddha.jec.model.TongContext;
import cn.buddha.jec.model.head.TongOutHead;
import cn.buddha.jec.util.TongBeanUtils;
import com.google.common.base.Stopwatch;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import javax.annotation.Resource;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName AbstractFlowable
 * @Author 友野浩二
 * @Date 2020/4/1 16:46
 * @Description 服务流程执行类
 * @Version 1.0
 */
@Slf4j
public abstract class AbstractFlowable<IN extends TongBaseRequest, OUT extends TongBaseResponse> implements IFlowable<IN, OUT> {

    /**
     * 业务逻辑处理之前的公共处理
     */
    @Resource
    private PreHandler preHandler;

    /**
     * 业务逻辑处理之后的公共处理
     */
    @Resource
    private PostHandler postHandler;

    /**
     * 业务逻辑处理过程中的异常处理
     */
    @Resource
    private FlowableExceptionHandler flowableExceptionHandler;

    /**
     * 平台事务管理器
     */
    @Resource
    private PlatformTransactionManager transactionManager;

    /**
     * 事务定义(属性)信息[事务隔离级别、事务传播机制、超时、只读、回滚机制],这里指定事务传播机制为
     */
    private final TransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);

    /**
     * @return OUT
     * @Author 友野浩二
     * @Description // 服务流程组件组织(业务逻辑处理)：将一个业务流程中的组件串联起来。
     * @Date 2020/4/1 16:50
     * @Param [input]
     */
    public abstract OUT middleExecute(IN input);

    /**
     * @return cn.buddha.jec.model.TongBaseResponse
     * @Author 友野浩二
     * @Description // 真正的业务逻辑处理(开启新的事务)
     * @Date 2020/4/1 21:13
     * @Param [input]
     * <p>
     * 介个方法的事务管理不起作用，新的方法见
     * @see cn.buddha.jec.flowable.core.AbstractFlowable#middleHandler(IN input)
     */
    @Deprecated
    @Transactional(rollbackFor = {Exception.class}, propagation = Propagation.REQUIRES_NEW)
    public OUT handler(IN input) {
        // 创建自动start的计时器(记录服务流程具体业务逻辑的耗时时间)
        Stopwatch stopwatch = Stopwatch.createStarted();

        OUT out = this.middleExecute(input);

        if (log.isInfoEnabled()) {
            log.info("服务流程业务逻辑代码[{}]耗时[{}ms]", TongContext.getInstance().getReqUrl(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }

        return out;
    }

    /**
     * @return cn.buddha.jec.model.TongBaseResponse
     * @Author 友野浩二
     * @Description // 真正的业务逻辑处理(开启新的事务)
     * @Date 2020/5/8 22:13
     * @Param [input]
     */
    public OUT middleHandler(IN input) {
        // 创建自动start的计时器(记录服务流程具体业务逻辑的耗时时间)
        Stopwatch stopwatch = Stopwatch.createStarted();

        // 获取一个事务,TransactionStatus: 记录事务的状态
        TransactionStatus transactionStatus = this.transactionManager.getTransaction(this.transactionDefinition);
        OUT out = null;

        try {
            out = this.middleExecute(input);
            this.transactionManager.commit(transactionStatus);
        } catch (Exception e) {
            this.transactionManager.rollback(transactionStatus);
            throw e;
        }

        if (log.isInfoEnabled()) {
            log.info("服务流程业务逻辑代码[{}]耗时[{}ms]", TongContext.getInstance().getReqUrl(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }

        return out;
    }

    /**
     * @return OUT
     * @Author 友野浩二
     * @Description // 服务流程执行真正的入口
     * @Date 2020/4/1 17:35
     * @Param [input]
     */
    @Override
    public OUT execute(String beanName, IN input) {
        // 创建自动start的计时器(对一次请求进行计时)
        Stopwatch stopwatch = Stopwatch.createStarted();

        // 1-1 业务逻辑处理之前的公共处理(框架层)
        this.preHandler.handler(input);

        // 1-2 业务逻辑处理之前的公共处理(具体业务代码层)
        this.preBusinessHandler(input);

        OUT output;

        try {
            // 2- 真正的业务逻辑处理
            // 2-1 开始执行每一个controller的方法
            // output = this.handler(input);
            output = this.middleHandler(input);

            // 2-2 响应模型处理(设置响应公共头信息[TongOutHead]和业务(请求头 / 响应头)分页信息[TongPageHead])
            output = this.getOutput(beanName, input, output);

            // 3-1 业务逻辑处理之后的公共处理(具体的业务逻辑层)
            this.postBusinessHandler(input);

            // 3-2 业务逻辑处理之后的公共处理(框架层)
            this.postHandler.handler(output);
        } catch (Exception e) {
            log.error("tong flowable business fail, exception: ", e);
            // 4- 异常处理
            this.flowableExceptionHandler.handler(e);
            // 这里抛出的异常会被全局异常处理类消化
            throw e;
        } finally {
            log.info("The [{}] flowable elapsedTime is [{}ms]", beanName, stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }

        return output;
    }

    /**
     * @return void
     * @Author 友野浩二
     * @Description // 业务逻辑处理之前的公共处理(具体业务逻辑代码层)
     * @Date 2020/4/20 20:25
     * @Param [tongBaseRequest]
     * <p>
     * 所有的前置处理可以由业务本身去考量，是否需要开启一个新的事务(当然前置处理一般都是一些数据准备或者基本校验,所以也不需要事务管理)
     */
    private void preBusinessHandler(TongBaseRequest tongBaseRequest) {
        // 创建自动start的计时器(记录服务流程业务前置通知的耗时时间)
        Stopwatch stopwatch = Stopwatch.createStarted();

        // 获取 IBusinessPreHandler(业务服务流程前置扩展接口) 的所有实现类
        List<IBusinessPreHandler> businessEffectivePreHandlerList = BusinessExtraFactory.getEffectiveExtra(IBusinessPreHandler.class);
        if (!TongBeanUtils.isNullObj(businessEffectivePreHandlerList)) {
            // 循环执行业务前置处理
            businessEffectivePreHandlerList.forEach(businessEffectivePreHandler -> {
                log.info("business pre handler, class name is [{}], component name is [{}]",
                        businessEffectivePreHandler.getClass().getName(),
                        businessEffectivePreHandler.getComponentName());
                // 干吧!
                businessEffectivePreHandler.beforeExecute(tongBaseRequest);
            });
        }

        if (log.isInfoEnabled()) {
            log.info("服务流程业务前置[{}]耗时[{}ms]", TongContext.getInstance().getReqUrl(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }
    }

    /**
     * @return void
     * @Author 友野浩二
     * @Description // 业务逻辑处理之后的公共处理(具体业务逻辑代码层)
     * @Date 2020/4/21 14:42
     * @Param [tongBaseRequest]
     * <p>
     * 所有的前置处理可以由业务本身去考量，是否需要开启一个新的事务(比如异步发短信就不需要事务,发送成功与否其实不是很重要)
     */
    private void postBusinessHandler(TongBaseRequest tongBaseRequest) {
        // 创建自动start的计时器(记录服务流程业务后置通知的耗时时间)
        Stopwatch stopwatch = Stopwatch.createStarted();

        // 获取 IBusinessPostHandler(业务服务流程后置扩展接口) 的所有实现类
        List<IBusinessPostHandler> businessEffectivePostHandlerList = BusinessExtraFactory.getEffectiveExtra(IBusinessPostHandler.class);
        if (!TongBeanUtils.isNullObj(businessEffectivePostHandlerList)) {
            // 循环执行业务后置处理
            businessEffectivePostHandlerList.forEach(businessEffectivePostHandler -> {
                log.info("business post handler, class name is [{}], component name is [{}]",
                        businessEffectivePostHandler.getClass().getName(),
                        businessEffectivePostHandler.getComponentName());
                // just do it!!!
                businessEffectivePostHandler.afterExecute(tongBaseRequest);
            });
        }

        if (log.isInfoEnabled()) {
            log.info("服务流程业务后置[{}]耗时[{}ms]", TongContext.getInstance().getReqUrl(), stopwatch.elapsed(TimeUnit.MILLISECONDS));
        }
    }

    /**
     * @return OUT
     * @Author 友野浩二
     * @Description // 响应模型处理
     * @Date 2020/4/5 17:00
     * @Param [beanName, input, output]
     */
    private OUT getOutput(final String beanName, final IN input, final OUT output) {
        OUT outputTemp = output;
        if (TongBeanUtils.isNullObj(outputTemp)) {
            // 针对 "真正的业务逻辑处理" 发生异常没有返回output的情况
            log.warn("The [{}] flow return no result!!!", beanName);

            // 返回此class所表示的实体的直接超类的type
            Type genericSuperclass = getClass().getGenericSuperclass();
            // 泛型参数
            Type[] actualTypeArguments = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
            log.info(actualTypeArguments.toString());
            log.info(Arrays.toString(actualTypeArguments));
            try {
                outputTemp = (OUT) ((Class) actualTypeArguments[1]).newInstance();
            } catch (InstantiationException e) {
                log.error("java.lang.InstantiationException fail", e);
            } catch (IllegalAccessException e) {
                log.error("java.lang.IllegalAccessException fail", e);
            }
        }

        // 响应模型设置 "响应公共头信息"
        TongOutHead tongOutHead = TongBusinessResult.success();
        TongContext tongContext = TongContext.getInstance();
        // 全局唯一序列号
        tongOutHead.setFlowableId(tongContext == null ? null : tongContext.getFlowableId());
        outputTemp.setTongOutHead(tongOutHead);
        if (!TongBeanUtils.isNullObj(input.getTongPageHead())) {
            // 响应模型设置 "业务(请求头 / 响应头)分页信息"
            outputTemp.setTongPageHead(input.getTongPageHead());
        }

        return outputTemp;
    }

}
